package org.stbernardpreserve.learning.effectivejava.hashcodeequalstostring;

import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.*;

import org.junit.Test;
import org.stbernardpreserve.learning.effectivejava.hashcodeequalstostring.Dog.IllegalDogStateException;

public class DogTestEqualsHashCodeAndToString {

	@Test
	public void testWeightValidation() {
		try {
			new Dog.DogBuilder().age(8).breed(Dog.Breed.STBERNARD)
					.name("Pearl").droolCoefficient(.75).build();
		} catch (IllegalDogStateException e) {
			return;
		}

		assertTrue("Failed to validate Dog", false);
	}

	@Test
	public void testAgeValidation() {
		try {
			new Dog.DogBuilder().weight(208).breed(Dog.Breed.STBERNARD)
					.name("Pearl").droolCoefficient(.75).build();
		} catch (IllegalDogStateException e) {
			return;
		}

		assertTrue("Failed to validate Dog", false);
	}

	@Test
	public void testBreedValidation() {
		try {
			new Dog.DogBuilder().weight(208).age(8).name("Pearl")
					.droolCoefficient(.75).build();
		} catch (IllegalDogStateException e) {
			return;
		}

		assertTrue("Failed to validate Dog", false);
	}

	@Test
	public void testToString() {
		Dog bernard = new Dog.DogBuilder().weight(208).age(8)
				.breed(Dog.Breed.STBERNARD).name("Pearl").droolCoefficient(.75)
				.build();

		assertThat(
				"Returned ToString is Correct",
				bernard.toString(),
				is("A St. Bernard Named Pearl who is 8 years old and weighs 208 pounds. A heavy drooler"));

		Dog bernard2 = new Dog.DogBuilder().weight(130).age(10)
				.breed(Dog.Breed.STBERNARD).name("Jack").droolCoefficient(.60)
				.build();

		assertThat(
				"Returned ToString is Correct",
				bernard2.toString(),
				is("A St. Bernard Named Jack who is 10 years old and weighs 130 pounds. A heavy drooler"));

		Dog rottweiler = new Dog.DogBuilder().weight(95).age(5)
				.breed(Dog.Breed.ROTTWEILER).name("George")
				.droolCoefficient(.20).build();

		assertThat(
				"Returned ToString is Correct",
				rottweiler.toString(),
				is("A Rottweiler Named George who is 5 years old and weighs 95 pounds. A light drooler"));

		Dog oldEnglishMastiff = new Dog.DogBuilder().weight(225).age(3)
				.breed(Dog.Breed.OLD_ENGLISH_MASTIFF).name("Buddy")
				.droolCoefficient(.5).build();

		assertThat(
				"Returned ToString is Correct",
				oldEnglishMastiff.toString(),
				is("A Old English Mastiff Named Buddy who is 3 years old and weighs 225 pounds. A medium drooler"));

	}

	@Test
	public void testEquals() {
		Dog bernard = new Dog.DogBuilder().weight(208).age(8)
				.breed(Dog.Breed.STBERNARD).name("Pearl").droolCoefficient(.75)
				.build();

		Dog rottweiler = new Dog.DogBuilder().weight(95).age(5)
				.breed(Dog.Breed.ROTTWEILER).name("George")
				.droolCoefficient(.20).build();

		assertFalse("Bernard Doesn't equal rottweiler", bernard.equals(rottweiler));

		// Reflexive
		assertTrue("Bernard Equals itself", bernard.equals(bernard));
		
		Dog bernard2 = new Dog.DogBuilder().weight(208).age(8)
				.breed(Dog.Breed.STBERNARD).name("Jack").droolCoefficient(.75)
				.build();
		
		// Symetric
		assertTrue("Bernard Equals Bernard2", bernard.equals(bernard2));
		assertTrue("Bernard2 Equals Bernard", bernard2.equals(bernard));

		Dog bernard3 = new Dog.DogBuilder().weight(208).age(8)
				.breed(Dog.Breed.STBERNARD).name("Phred").droolCoefficient(.35)
				.build();
		
		// Transitive
		assertTrue("Bernard Equals Bernard2", bernard.equals(bernard2));
		assertTrue("Bernard Equals Bernard3", bernard.equals(bernard3));
		assertTrue("Bernard2 Equals Bernard3", bernard2.equals(bernard3));
		
		// Consistent
		assertTrue("Bernard Equals Bernard2", bernard.equals(bernard2));
		assertTrue("Bernard2 Equals Bernard", bernard2.equals(bernard));
		assertTrue("Bernard Equals Bernard2", bernard.equals(bernard2));
		assertTrue("Bernard2 Equals Bernard", bernard2.equals(bernard));

		// null values
		assertFalse("Bernard Equals Bernard2", bernard.equals(null));

	}

	@Test
	public void testHashCode() {
		Dog bernard = new Dog.DogBuilder().weight(208).age(8)
				.breed(Dog.Breed.STBERNARD).name("Pearl").droolCoefficient(.75)
				.build();
		Dog bernard2 = new Dog.DogBuilder().weight(208).age(8)
				.breed(Dog.Breed.STBERNARD).name("Jack").droolCoefficient(.75)
				.build();

		assertThat("Bernard HashCode equals Bernard2 Hashcode", bernard.hashCode(),is(bernard2.hashCode()));


		Dog rottweiler = new Dog.DogBuilder().weight(95).age(5)
				.breed(Dog.Breed.ROTTWEILER).name("George")
				.droolCoefficient(.20).build();

		assertFalse("Bernard Doesn't equal rottweiler", bernard.equals(rottweiler));
		assertThat("Bernard hashCode Doesn't equal rottweiler hashcode", bernard.hashCode(), not(rottweiler.hashCode()));

		Dog oldEnglishMastiff = new Dog.DogBuilder().weight(225).age(3)
				.breed(Dog.Breed.OLD_ENGLISH_MASTIFF).name("Buddy")
				.droolCoefficient(.5).build();

		assertFalse("Bernard Doesn't equal old english mastiff", bernard.equals(oldEnglishMastiff));
		assertThat("Bernard hashCode Doesn't equal old english mastiff hashcode", bernard.hashCode(), not(oldEnglishMastiff.hashCode()));
	}

}
